home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / CMRefOps.c < prev    next >
Encoding:
Text File  |  1996-08-28  |  54.2 KB  |  1,229 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:           CMRefOps.c   
  3.  
  4.     Contains:    Container Manager Object Reference Operations
  5.  
  6.     Owned by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <3>     8/13/96    DM        1362809: disable containers on error
  15.          <2>     1/15/96    TJ        Cleaned Up
  16.          <6>     6/29/95    DM        #1263765 change sizeof(orgSize) to orgSize
  17.                                     for buffer allocation in CMSetReference
  18.          <5>     5/11/95    DM        1214394, 1245829: stop compiler warnings
  19.          <4>     2/17/95    EL        1182275: Forgot to move in CMKeepObject in
  20.                                     last checkin.
  21.          <3>     2/15/95    EL        1182275: Move CMKeepObject to CMRefOps and
  22.                                                     CMKeepObject would now ignore referenced
  23.                                                     objects that cannot be found.
  24.          <2>     8/26/94    EL        #1182308 Updating reference object should
  25.                                                     use the endian-ness of the reference object
  26.                                                     and not the endian-ness of the updating
  27.                                                     container.
  28.          <3>     5/10/94    EL        #1162327. Need to delete the value rather
  29.                                                     than just delete the data.
  30.          <2>     5/10/94    EL        #1162327. When a value is moved from one
  31.                                                     container to another and there is value in
  32.                                                     , the data is now copied over.
  33.          <1>      2/3/94    EL        first checked in
  34.          <6>      1/6/94    EL        Do not free a value if it belongs to two
  35.                                                     containers.
  36.          <5>    12/13/93    EL        Temp fix for case where the RefDataObject
  37.                                                     is deleleted when its length reach 0, but
  38.                                                     during playback of update the value header
  39.                                                     would not know the RefDataObject is now
  40.                                                     gone.
  41.          <4>     12/7/93    EL        Add CMGetReferenceForObject call.
  42.          <3>     10/6/93    EL        Rename CMGetAdjacentXXXX to GetAdjacentXXXX
  43.                                                     and static near.
  44.          <2>     10/4/93    EL        Fix bug in CMDeleteReference, Fix bug of
  45.                                                     reference of old value in updating
  46.                                                     container.
  47.         
  48. */
  49.  
  50. /*---------------------------------------------------------------------------*
  51.  |                                                                           |
  52.  |                         <<<    CMRefOps.c    >>>                          |
  53.  |                                                                           |
  54.  |               Container Manager Object Reference Operations               |
  55.  |                                                                           |
  56.  |                               Ira L. Ruben                                |
  57.  |                                 10/15/92                                  |
  58.  |                                                                           |
  59.  |                     Copyright Apple Computer, Inc. 1992-1994              |
  60.  |                           All rights reserved.                            |
  61.  |                                                                           |
  62.  *---------------------------------------------------------------------------*
  63.  
  64.  An object's value may contain data that refers to other objects.  This file contains
  65.  all the API routines that allow a user to embed object ID references in value data.  The
  66.  routines defined here allow that data to be manipulated without the API user explicitly 
  67.  knowing the form such object references take.
  68.  
  69.  But, just for the record, we better describe how the references are maintained here!
  70.  
  71.  When a reference is to be saved in some value, the user calls CMNewReference() or 
  72.  CMSetReference().  A reference key is to be associated with some object.  The user 
  73.  retrieves objects via their keys.  We record the key/object (ID) associations.  They
  74.  are recorded in a private recording object which is "tied" to the user's value from a
  75.  field (refDataObject) in the value header.
  76.  
  77.  The recording object has one special property and one specially typed value for that
  78.  property.  The value data for that value is the list of key/object ID associations.  They
  79.  are formatted as 8-byte entries; 4 bytes for the key (which comes first), and 4 bytes for
  80.  the corresponding object ID.
  81.  
  82.  This data is a linear list on the assumption there won't be "too many" references in each
  83.  value.
  84.  
  85.  The list is maintained as data because it is subject to updating like any other data!
  86.  The price we pay for this, however, is to do handler I/O to read and write this data.
  87.  To gain some efficiency, buffered I/O, using the routines in  BufferIO.c , is used.
  88. */
  89.  
  90.  
  91. #include <stddef.h>
  92. #include <string.h>
  93. #include <setjmp.h>
  94. #include <stdio.h>
  95.  
  96. #ifndef __CMTYPES__
  97. #include "CMTypes.h"
  98. #endif
  99. #ifndef __CM_API__
  100. #include "CMAPI.h"
  101. #endif
  102. #ifndef __TOCENTRIES__
  103. #include "TOCEnts.h"   
  104. #endif
  105. #ifndef __TOCOBJECTS__
  106. #include "TOCObjs.h"   
  107. #endif
  108. #ifndef __GLOBALNAMES__
  109. #include "GlbNames.h"   
  110. #endif
  111. #ifndef __CONTAINEROPS__
  112. #include "Containr.h"  
  113. #endif
  114. #ifndef __HANDLERS__
  115. #include "Handlers.h"
  116. #endif
  117. #ifndef __BUFFEREDIO__
  118. #include "BufferIO.h"  
  119. #endif
  120. #ifndef __LISTMGR__
  121. #include "ListMgr.h"
  122. #endif
  123. #ifndef __REFERENCES__
  124. #include "Refs.h"      
  125. #endif
  126. #ifndef __SESSIONDATA__
  127. #include "Session.h"          
  128. #endif
  129. #ifndef __ERRORRPT__
  130. #include "ErrorRpt.h"      
  131. #endif
  132. #ifndef __UTILITYROUTINES__
  133. #include "Utility.h"        
  134. #endif
  135.                                                                     
  136.                                                                     CM_CFUNCTIONS
  137.  
  138. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  139. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  140. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  141. /* choke compilers that don't recognize them.                                                                                        */
  142.  
  143. #if CM_MPW
  144. #pragma segment CMReferenceOps
  145. #endif
  146.  
  147.  
  148. /* The references are always searched for based on the reference key in the data.  One    */
  149. /* common routine, getReference(), is used to do this searhing.  It returns the                 */
  150. /* following status codes to indicate the results of its search.                                                */
  151.  
  152. enum RefSearchStatus {                                                /* getReference() search status:                    */
  153.     RefReadError,                                                                /*        some read error occurred                        */
  154.     RefFound,                                                                        /*        returned offset == position & ID        */
  155.     RefNotFound                                                                    /*         offset == list size, ID undefined        */ 
  156. };
  157. typedef enum RefSearchStatus RefSearchStatus;
  158.  
  159.  
  160. #if CMSHADOW_LIST
  161. /*----------------------------------------------------------------------------------------*
  162.  | appendShadowListEntry - create/append in a single shadow list entry to its shadow list |
  163.  *----------------------------------------------------------------------------------------*
  164.  
  165.  This internal routine creates and then appends a single shadow list entry to the specified
  166.  shadow list.  The list header pointer, key, and its associated object ID are passed.  The
  167.  container is used for allocating the entry and error reporting.
  168.  
  169.  The function returns a pointer to the created entry.  NULL is returned if an error is
  170.  reported for an allocation failure and the error reporter returns.
  171. */
  172.  
  173. static RefDataShadowEntryPtr CM_NEAR CM_PASCAL appendShadowListEntry(ContainerPtr container,
  174.                                                                                                                                          ListHdrPtr refShadowList,
  175.                                                                                                                                          CM_ULONG key,
  176.                                                                                                                                           CMObjectID objectID)
  177. {
  178.     RefDataShadowEntryPtr refShadowEntry;
  179.     
  180.     refShadowEntry = (RefDataShadowEntryPtr)CMmalloc(sizeof(RefDataShadowEntry));
  181.     if (refShadowEntry == NULL) {
  182.         Container_Disable(container);
  183.         ERROR1(CM_err_NoRefShadowList, CONTAINERNAME);
  184.         return (NULL);
  185.     }
  186.     
  187.     cmNullListLinks(refShadowEntry);
  188.     refShadowEntry->key          = key;                                    
  189.     refShadowEntry->objectID = objectID;
  190.     
  191.     return ((RefDataShadowEntryPtr)cmAppendListCell(refShadowList, refShadowEntry));
  192. }
  193.  
  194.  
  195. /*----------------------------------------------------------------------------*
  196.  | deleteShadowListEntry - delete a single recording object shadow list entry |
  197.  *----------------------------------------------------------------------------*
  198.  
  199.  This internal routine is used to shadow CMDeleteReference(), i.e., to delete the
  200.  corresponding shadow list entry.  The recording object's value header and the pointer to
  201.  the entry to be deleted are passed.
  202.  
  203.  The pointer is the value returned by getReference() when CMDeleteReference() called it to
  204.  find the reference entry to be deleted.
  205.  
  206.  If the last list entry is deleted, the list header itself is deleted and the pointer in
  207.  the value header set to NULL to indicate there is no shadow list.
  208. */
  209.  
  210. static void CM_NEAR CM_PASCAL deleteShadowListEntry(TOCValueHdrPtr refDataValueHdr,
  211.                                                                                                         RefDataShadowEntryPtr refShadowEntry)
  212. {
  213.     ContainerPtr container = refDataValueHdr->container->targetContainer;
  214.     ListHdrPtr      refShadowList = RefShadowList(refDataValueHdr);
  215.     
  216.     if (refShadowList != NULL && refShadowEntry != NULL) {
  217.         CMfree(cmDeleteListCell(refShadowList, refShadowEntry));    /* delete the entry                */
  218.         if (cmIsEmptyList(refShadowList)) {                                                /* if no more entries...    */
  219.             CMfree(refShadowList);                                                                    /* ...delete the list hdr    */
  220.             RefShadowList(refDataValueHdr) = NULL;                                    /* ...indicate no list        */
  221.         }
  222.     }
  223. }
  224.  
  225.  
  226. /*-----------------------------------------------------------------------------*
  227.  | cmDeleteRefDataShadowList - delete an entire recording object's shadow list |
  228.  *-----------------------------------------------------------------------------*
  229.  
  230.  This routine is called to delete the entire shadow list pointed to from the specified
  231.  recording object's value header.  It is used for clearing the list during error recovery
  232.  and value (header) deletions.
  233.  
  234.  Note, generally the caller should have done a HasRefShadowList(refDataValueHdr) prior to
  235.  calling this routine to make sure that the value header is indeed a recording object
  236.  value header.
  237. */
  238.  
  239. void cmDeleteRefDataShadowList(TOCValueHdrPtr refDataValueHdr)
  240. {
  241.     ContainerPtr                    container = refDataValueHdr->container->targetContainer;
  242.     ListHdrPtr                         refShadowList = RefShadowList(refDataValueHdr);
  243.     RefDataShadowEntryPtr refShadowEntry, nextEntry;
  244.     
  245.     if (refShadowList != NULL) {
  246.         refShadowEntry = (RefDataShadowEntryPtr)cmGetListHead(refShadowList);
  247.         
  248.         while (refShadowEntry) {                                            /* delete all the list entries...            */
  249.             nextEntry = (RefDataShadowEntryPtr)cmGetNextListCell(refShadowEntry);
  250.             CMfree(refShadowEntry);
  251.             refShadowEntry = nextEntry;
  252.         }
  253.         
  254.         CMfree(refShadowList);                                                /* delete the list header                            */
  255.         RefShadowList(refDataValueHdr) = NULL;                /* indicate there's no shadow list        */
  256.     }
  257. }
  258. #endif
  259.  
  260.  
  261. /*---------------------------------------------------------------------------*
  262.  | getRecordingValueHdr    - get recording object's reference list value header |
  263.  *---------------------------------------------------------------------------*
  264.  
  265.  This is an internal routine used to return the value (header) pointer (refNum) for the
  266.  recording object's object/reference association list.  NULL is returned if the recording
  267.  object doesn't exist or an error is reported and the error reporter returns.
  268.  
  269.  The only possible errors here are that the property or the property's value header do not
  270.  exist in the recording object.  This shoould never happen.  But we check it anyway.
  271. */
  272.  
  273. static TOCValueHdrPtr CM_NEAR CM_PASCAL getRecordingValueHdr(TOCValueHdrPtr theValueHdr)
  274. {
  275.     ContainerPtr      container;
  276.     TOCPropertyPtr refDataProperty;
  277.     TOCValueHdrPtr refDataValueHdr;
  278.  
  279.     if (!HasRefDataObject(theValueHdr))                                            /* if no recording object...    */
  280.         return (NULL);                                                                                /* ...not found                                */
  281.         
  282.     /* Validate that we have the "proper" property and type in the recording object...        */
  283.     /* Call me paranoid (ok, you're paranoid!)                                                                                        */
  284.     
  285.     refDataProperty = cmGetObjectProperty(RefDataObject(theValueHdr), CM_StdObjID_ObjReferences);
  286.     if (refDataProperty) {
  287.         refDataValueHdr = (TOCValueHdrPtr)cmGetListHead(&refDataProperty->valueHdrList);
  288.         if (refDataValueHdr && refDataValueHdr->typeID != CM_StdObjID_ObjRefData)
  289.             refDataValueHdr = NULL;
  290.     }
  291.     if (refDataProperty == NULL || refDataValueHdr == NULL){/* gee, what went wrong?            */
  292.         container = theValueHdr->container->targetContainer;    /* this is an internal error!    */
  293.         /* do not disable container: Container_Disable(container);*/
  294.         ERROR1(CM_err_Internal6, CONTAINERNAME);                          
  295.         return (NULL);
  296.     }
  297.     
  298.     return (refDataValueHdr);                                                                /* return recording value hdr    */
  299. }
  300.  
  301.  
  302. /*----------------------------------------------------------------------*
  303.  | getReference - find a reference recorded in a value's reference list |
  304.  *----------------------------------------------------------------------*
  305.  
  306.  This is an internal routine used to search a recording object's reference list for 
  307.  theReferenceData "key". The reference list is value data for the specified refDataValueHdr
  308.  (the value header for the recording object's reference list).  The result of the search
  309.  is returned as the function result along with an offset and object ID.  The meaning of the
  310.  returned key, objectID, and refShadowEntry are a function of the returned search status as
  311.  follows:
  312.    
  313.      RefFound:         The key was found.
  314.                                  key                        = internal (hardware) representation of the CMReference key.
  315.                                  objectID             = object ID associated with the found key.
  316.                                  refShadowEntry = ptr to found shadow list entry (NULL if no shadow list).
  317.                                  offset               = value data offset to the key.
  318.  
  319.      RefNotFound:     The key was not found.
  320.                                  key                        = internal (hardware) representation of the CMReference key.
  321.                                  objectID             =    undefined.
  322.                                  refShadowEntry = NULL.
  323.                                  offset               = current size of the value data.  This allows the caller
  324.                                                                      to append the new reference if necessary.
  325.      
  326.      RefReadError: A read error has occurred and the error reporter returned.  Caller must
  327.                                   not "go on".
  328.                                  
  329.  Note, the returned key is the hardware (unsigned long) representation of the input
  330.  CMReference theReferenceData key.  This is converted here to make it easier to work with.
  331.  So for convenience it is given back to the caller.  However, the caller may have already
  332.  did this for the same reason.  So, by convention, if the key is PASSED IN as 0, the
  333.  conversion is done here.  If it is nozero, it is assumed the caller did it, and the key
  334.  is used as passed, i.e., it is assumed that theReferenceData was converted to key.
  335.  
  336.                                                                  The Shadow List
  337.                                                                 ===============
  338.                                                             
  339.  Under configuation option (CMSHADOW_LIST) a shadow list is created here.  The "shadow 
  340.  list" is a copy of the actual value data (thus it "shadows" the data - tricky name 'eh?).
  341.  All changes to the data are shadowed in the list.  But the list is in internal    (hardware)
  342.  format for the keys and object IDs and in memory to make these searches we do here more
  343.  efficient than reading it each time.  Of course, we have to read it the first time.
  344.  
  345.  Normally invertSerach is false and we search in the normal way. However, if invertSerach
  346.  is true, we invert the search to find a reference for the given object ID
  347. */
  348.  
  349. static RefSearchStatus CM_NEAR CM_PASCAL getReference(TOCValueHdrPtr refDataValueHdr,
  350.                                                                                                             CMReference theReferenceData,
  351.                                                                                                             CM_ULONG *offset,
  352.                                                                                                             CM_ULONG *key,
  353.                                                                                                             CMObjectID *objectID,
  354.                                                                                                             CMBoolean invertSearch,
  355.                                                                                                             RefDataShadowEntryPtr *refShadowEntry)
  356. {
  357.     ContainerPtr                    container = refDataValueHdr->container;
  358.     CM_ULONG                           x, currKey, size = refDataValueHdr->size;
  359.     CMObjectID                        currObjectID;
  360.     RefSearchStatus             searchStatus = RefNotFound;
  361.     void                                    *ioBuffer = NULL;
  362.     jmp_buf                              getRefEnv;
  363.     #if CMSHADOW_LIST
  364.     ListHdrPtr                         refShadowList;
  365.     RefDataShadowEntryPtr r;
  366.     #endif
  367.     
  368.     /* The user's key is a CMReference which are awkward to work with internally.  So all    */
  369.     /* manipulations of the key are done in internal (i.e., hardware) format.  To avoid        */
  370.     /* unnecessarily doing this conversion a second time if the caller already did it,         */
  371.     /* the key can be passed as non-zero.  Zero indicates we are to convert it here.            */
  372.     
  373.     if (!invertSearch)
  374.         if (*key == 0)                                                                                /* if caller didn't supply key*/
  375.             CMextractData(container, theReferenceData, -4, key);/* ...convert key to internal    */
  376.     
  377.     /* If the shadow list already exists, search it now since we know it's in sync                 */
  378.     /* (hopefully) with the recording object's actual reference list value data.                    */
  379.     
  380.     #if CMSHADOW_LIST
  381.     if (RefShadowList(refDataValueHdr) != NULL) {                        /* if shadow lists exists...    */
  382.         r = (RefDataShadowEntryPtr)cmGetListHead(RefShadowList(refDataValueHdr));
  383.         *offset = 0;                                                                                    /* maintain value data offset    */
  384.         
  385.         while (r) {                                                                                        /* search the list...                    */
  386.             if (invertSearch) {
  387.                 if (r->objectID == *objectID) {
  388.                     *key = r->key;
  389.                     *refShadowEntry = r;                                                        /* ...return shadow entry ptr    */
  390.                     return(RefFound);                                                                /* ...return key found status    */
  391.                 }
  392.             } else {
  393.                 if (r->key == *key) {                                                            /* ...if the key is found...    */
  394.                     *objectID = r->objectID;                                                /* ...return its object ID        */
  395.                     *refShadowEntry = r;                                                        /* ...return shadow entry ptr    */
  396.                     return(RefFound);                                                                /* ...return key found status    */
  397.                 }
  398.             }
  399.             r = (RefDataShadowEntryPtr)cmGetNextListCell(r);        /* ...get next shadow entry        */
  400.             *offset += sizeof(ReferenceData);                                        /* ...account for value data    */
  401.         }
  402.         
  403.         *refShadowEntry = NULL;                                                                /* the key was not found            */
  404.         
  405.         return (RefNotFound);                                                                    
  406.     }
  407.     
  408.     /* If the shadow list doesn't exist for the recording value create it now.  First the */
  409.     /* list header which is pointed to from the value's header.  The pointer is the same     */
  410.     /* field as the recording object pointer. It is a union to give it a more appropriate */
  411.     /* name. We always know the difference because the recording object's value header is    */
  412.     /* uniquely typed.                                                                                                                                        */
  413.     
  414.     refShadowList = RefShadowList(refDataValueHdr) = (ListHdrPtr)CMmalloc(sizeof(ListHdr));
  415.     if (refShadowList == NULL) {
  416.         /* do not disable container: Container_Disable(container);*/
  417.         ERROR1(CM_err_NoRefShadowList, CONTAINERNAME);
  418.         return (RefReadError);
  419.     }
  420.     cmInitList(refShadowList);
  421.     #endif
  422.  
  423.     /* There's nothing to do if there are no references.  If we were called from                     */
  424.     /* CMSetReference(), it will create the first entry.                                                                    */
  425.     
  426.     if (size == 0) {                                                                                /* if no data to read...            */
  427.         *offset = 0;                                                                                    /* ...return current size            */
  428.         *refShadowEntry = NULL;                                                                /* ...no shadow entry                 */
  429.         return (RefNotFound);                                                                    /* ...return key not found        */
  430.     }
  431.  
  432.     /* Read in the reference data to build the shadow list this first time if the config    */
  433.     /* allows it. All further entry modifications are done by the callers to shadow their */
  434.     /* respective operations to the recording value data in the list.  While we're at it, */
  435.     /* we do the required search for the desired key.     If we're not building the shadow        */
  436.     /* list, we can break out of the loop as soon as we find the key.  Otherwise, the         */
  437.     /* entire list must be read in.                                                                                                                */
  438.     
  439.     /* Note, in a (crude) attempt to make this read and search more efficient (after all     */
  440.     /* we have to read read value data here), we use buffered value I/O.                                    */
  441.     /* See  BufferIO.c  for further details on how buffered value data I/O works.                    */
  442.     
  443.     if (setjmp(getRefEnv)) {                                                                /* ...set for buffering errors*/
  444.         cmReleaseIOBuffer(ioBuffer);                                                    /* ...if longjmp taken to here*/
  445.         #if CMSHADOW_LIST
  446.         cmDeleteRefDataShadowList(refDataValueHdr);
  447.         #endif
  448.         /* do not disable container: Container_Disable(container);*/
  449.         ERROR1(CM_err_BadRefRead, CONTAINERNAME);                            /* ...we had an read error        */
  450.         return (RefReadError);
  451.     }
  452.     
  453.     ioBuffer = cmUseIOBuffer(container, RefsBufSize, (jmp_buf *)&getRefEnv); /*define bufr*/
  454.     if (ioBuffer == NULL) {
  455.         #if CMSHADOW_LIST
  456.         cmDeleteRefDataShadowList(refDataValueHdr);
  457.         #endif
  458.         return (RefReadError);
  459.     }
  460.     
  461.     cmNewBufferedInputData(ioBuffer, refDataValueHdr, refDataValueHdr->size);
  462.         
  463.     for (x = 0; x < size; x += 8) {                                                    /* find the reference (key)...*/
  464.         currKey          = (CM_ULONG)GET4Direct(ioBuffer);                /* ...read next key                        */
  465.         currObjectID = (CMObjectID)GET4(ioBuffer);                        /* ...its assoc. ID follows        */
  466.         
  467.         #if CMSHADOW_LIST
  468.         r = appendShadowListEntry(container, refShadowList, currKey, currObjectID);
  469.         if (r == NULL) {
  470.             cmDeleteRefDataShadowList(refDataValueHdr);
  471.             cmReleaseIOBuffer(ioBuffer);
  472.             return (RefReadError);
  473.         }
  474.         #endif
  475.         
  476.         if (invertSearch) {                                                                        /* we look for object ID            */
  477.             if (currObjectID != *objectID)                                            /* not found yet                            */
  478.                 continue;                                                                                    /* then keep looking                    */
  479.             *key = currKey;                                                                            /* found, get assoc. key            */
  480.         } else {                                                                                            /* we look for key                        */
  481.             if (currKey != *key)                                                                /* not found yet                            */
  482.                 continue;                                                                                    /* then keep looking                    */
  483.             *objectID = currObjectID;                                                        /* found, get assoc. object ID*/
  484.         }
  485.         searchStatus = RefFound;                                                            /* ...we found it!                        */
  486.         *offset = x;                                                                                    /* ...return value data offset*/
  487.         #if CMSHADOW_LIST
  488.         *refShadowEntry = r;                                                                    /* ...set ptr if shadowing        */
  489.         #else
  490.         *refShadowEntry = NULL;                                                                /* ...NULL if not shadowing        */
  491.         #endif
  492.         break;                                                                                                /* ...found, we are done            */
  493.     } /* for */                                                                                            /* ...keep reading                        */
  494.         
  495.     cmReleaseIOBuffer(ioBuffer);                                                        /* done searching and reading    */
  496.     
  497.     if (searchStatus == RefNotFound) {                                            /* if the key wasn't found...    */
  498.         *offset = size;                                                                                /* ...ret end of list position*/
  499.         *refShadowEntry = NULL;                                                                /* ...no shadow entry                    */
  500.     }
  501.     
  502.     return (searchStatus);                                                                    /* return search status                */
  503. }
  504.  
  505.         
  506. /*----------------------------------------------------------------*
  507.  | CMNewReference - define a "reference" to an object for a value |
  508.  *----------------------------------------------------------------*
  509.  
  510.  Creates a "reference" to the referencedObject and places it in theReferenceData.  The
  511.  (input) pointer to theReferenceData is returned.  It is ASSUMED that this data will be
  512.  written as (part of) the value data for the specified value.  The size of this data is
  513.  determined by the size of the CMReference type, i.e., sizeof(CMReference).
  514.  
  515.  A side effect of this routine is the creation of a (private to the Container Manager)
  516.  object (for the first reference) that records unique object/reference associations for the
  517.  passed value.  What is returned is a "key" that is recorded as the "reference" to allow
  518.  retrevial (e.g., using CMGetReferencedObject()) of the object (refNum) corresponding to a
  519.  reference (key).
  520.   
  521.  Calling CMNewReference() for a already existing reference (key) simply sets
  522.  theReferenceData and returns its pointer.
  523.  
  524.  Note, that CMNewReference() always defines theReferenceData key. However, CMSetReference()
  525.  can be called instead to define, or redefine, an ARBITRARY key (still of type CMReference)
  526.  to associate with an object.  See CMSetReference() for further details.
  527. */
  528.  
  529. CMReference CM_PTR * CM_FIXEDARGS CMNewReference(CMValue value,
  530.                                                                                                  CMObject referencedObject,
  531.                                                                                                   CMReference CM_PTR theReferenceData)
  532. {
  533.     ContainerPtr container;
  534.     ContainerPtr targetContainer;
  535.     
  536.     ExitIfBadValue(value, NULL);                                                    /* validate value                                */
  537.     ExitIfBadObject(referencedObject, NULL);                            /* validate referencedObject        */
  538.  
  539.     container = ((TOCValueHdrPtr)value)->container->updatingContainer;
  540.     targetContainer = ((TOCValueHdrPtr)value)->container->targetContainer;
  541.     
  542.     { ContainerPtr other = ((TOCObjectPtr)referencedObject)->container;
  543.         if (targetContainer != other->targetContainer) {
  544.             Container_Disable(container);
  545.             Container_Disable(other);
  546.             ERROR2(CM_err_CantReference, CONTAINERNAMEx(container),
  547.                                                                      CONTAINERNAMEx(other));
  548.             return (NULL);
  549.         }
  550.     }
  551.  
  552.     /* A CMObjectID for some hardware implementations may NOT be exactly 4 bytes (but it    */
  553.     /* better be at least 4 bytes).  A user's "reference" data is always 4 bytes because    */
  554.     /* we define such data here as an object ID.  We must take care to convert the                 */
  555.     /* internal, i.e., hardware, representation to that of a container representation. By    */
  556.     /* share "luck"  we just happen to have a handler that does this conversion.                    */
  557.     
  558.     CMformatData(container, theReferenceData, -4, &((TOCObjectPtr)referencedObject)->objectID);
  559.  
  560.     /* Since we have defined the CMReference key here, we can call CMSetReference() just     */
  561.     /* like the user to (re)define the object reference with this key.                                        */
  562.     
  563.     return (CMSetReference(value, referencedObject, theReferenceData));
  564. }
  565.  
  566.         
  567. /*--------------------------------------------------------------------*
  568.  | CMSetReference - (re)define a "reference" to an object for a value |
  569.  *--------------------------------------------------------------------*
  570.  
  571.  This is similar to CMNewReference() except that here the caller defines the CMReference
  572.  key to associate with an object.  The specified key must not be a nonzero value.  The
  573.  (input) pointer to theReferenceData key is returned.  
  574.  
  575.  In all cases the specified CMReference key is associated with the specified
  576.  referencedObject.  The associations are recorded in the private recording object for the
  577.  passed value.  New references are recorded, and previously existing references (i.e.,
  578.  theReferenceData key matches one of the previously recorded keys) are CHANGED to assoicate
  579.  it with the (new) referencedObject.
  580.  
  581.  The only difference between CMNewReference() and CMSetReference() is that with 
  582.  CMNewReference(), the Container Manager defines the CMReference key, while with
  583.  CMSetReference() the caller defines the key.  The net result is the same; the keys are
  584.  recorded in the value's recording object to define the association to the specified
  585.  referenced object.
  586.  
  587.  Note, that multiple references to the SAME object can be recorded by passing different
  588.  keys (theReferenceData).
  589.  
  590.  Once these associations are recorded, they may be counted, deleted, and accessed using
  591.  CMCountReferences(), CMDeleteReference(), and CMGetNextReference() respectively.
  592. */
  593.  
  594. CMReference CM_PTR * CM_FIXEDARGS CMSetReference(CMValue value,
  595.                                                                                                  CMObject referencedObject,
  596.                                                                                                   CMReference CM_PTR theReferenceData)
  597. {
  598.     ContainerPtr                      container;
  599.     ContainerPtr                      targetContainer;
  600.     TOCObjectPtr                     refDataObject, refedObject;
  601.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  602.     CM_ULONG                          key, offset, orgSize, amountRead;
  603.     CMObjectID                          objectID;
  604.     RefDataShadowEntryPtr refShadowEntry;
  605.     ReferenceData                     refData;
  606.     CM_CHAR                                *tempBuf;
  607.     
  608.     ExitIfBadValue(value, NULL);                                                    /* validate value                                */
  609.     ExitIfBadObject(referencedObject, NULL);                            /* validate referencedObject        */
  610.  
  611.     theValueHdr = (TOCValueHdrPtr)value;
  612.     container     = theValueHdr->container->updatingContainer;
  613.     targetContainer     = theValueHdr->container->targetContainer;
  614.     refedObject    = (TOCObjectPtr)referencedObject;
  615.     
  616.     if (targetContainer != refedObject->container->targetContainer) {
  617.         Container_Disable(refedObject->container);
  618.         Container_Disable(container);
  619.         ERROR2(CM_err_CantReference, CONTAINERNAMEx(targetContainer),
  620.                                                                  CONTAINERNAMEx(refedObject->container));
  621.         return (NULL);
  622.     }
  623.     
  624.     /* The list of references is recorded in a private object associated with the user's    */
  625.     /* value (header).  The reference data object is "tied" to the user's value through a */
  626.     /* pointer in the user's value header.  Of course, the first such reference must             */
  627.     /* create the recording object and tie it to the user's value header.                                    */
  628.     
  629.     /* Since an object is created, the user would see it if if s/he walked the TOC with        */
  630.     /* CMGetNextObject().  But this is to be a private object.  So to stop the user from    */
  631.     /* seeing it, it is unlinked from the object master chain which is used by the                */
  632.     /* CMGetNextObject().                                                                                                                                    */
  633.     
  634.     /* Note that a value move moves a value header and hence the recording object will         */
  635.     /* "go along for the ride".  Property and value deletes, however, must make sure to     */
  636.     /* delete this object.                                                                                                                                */
  637.     
  638.     if (!HasRefDataObject(theValueHdr)) {                                    /* if 1st ref for this value...    */
  639.  
  640.         /* if the value is not part of the updating container but in some target container  */
  641.         /* then the reference is not attached to the value since the TOC in the target             */
  642.         /* will not be updated, and since we only have an update value on the updating            */
  643.         /* container, there is no place to attach the reference in the TOC, so what we do        */
  644.         /* is a kludge, we delete the value and then insert it back into updating container.*/
  645.         /* Now we can attach the reference to new value. However, we do not want to really    */
  646.         /* delete the value because if we create a new value header it would have a                    */
  647.         /* different refnum. Also we need to copy the data from the target. Instead we just */
  648.         /* record deleted in the touch list by using cmTouchDeletedValue so it would show        */
  649.         /* up in the update value, and then we just declare it to belong to the updating        */
  650.         /* container, effectively moving it to the updating container                                                */
  651.  
  652.         if (theValueHdr->container != container) {
  653.             orgSize = theValueHdr->size;
  654.             if (orgSize) {
  655.                 /* save a copy of the original data                                                                                         */
  656.                 tempBuf = (CM_CHAR *)CMmalloc(orgSize); // DMc - orgSize, not sizeof(orgSize)
  657.                 if (tempBuf == NULL) return (NULL);                            /* exit if failure                            */
  658.                 amountRead = CMReadValueData((CMValue)theValueHdr, tempBuf, 0, orgSize);
  659.                 if (amountRead != orgSize) {
  660.                     CMfree(tempBuf);                                                            /* exit if fail to read                    */
  661.                     return (NULL);
  662.                 }
  663.                 CMDeleteValueData((CMValue)theValueHdr, 0, orgSize);
  664.             }
  665.             /* What we are doing is to delete the old value from the old container and then   */
  666.             /* insert it into the new container, but then we would change theValueHdr.                */
  667.             /* So we just delete the data and cheat by changing the container                                    */
  668.             cmTouchDeletedValue(theValueHdr, theValueHdr->theProperty->theObject);
  669.             theValueHdr->container = container;
  670.             if (orgSize) {
  671.                 /* write back the original data                                                                                                 */
  672.                 CMInsertValueData((CMValue)theValueHdr, tempBuf, 0, orgSize);
  673.                 CMfree(tempBuf);                                                            
  674.             }
  675.         }
  676.         
  677.         refDataObject = cmDefineObject(container,                        /* ...create recording object        */
  678.                                                                      container->nextUserObjectID,
  679.                                                                      CM_StdObjID_ObjReferences,
  680.                                                                      CM_StdObjID_ObjRefData,
  681.                                                                      NULL,
  682.                                                                      container->generation, 0, 
  683.                                                                      (ObjectObject | ProtectedObject),
  684.                                                                      &refDataValueHdr);    
  685.         if (refDataObject == NULL) return (NULL);                        /* exit if failure                            */
  686.         cmUnlinkObject(container->toc, refDataObject);            /* unlink from master chain            */
  687.         refDataObject->useCount = 1;                                                /* initial use of this object        */
  688.         IncrementObjectID(container->nextUserObjectID);            /* set to use next available ID    */
  689.         refDataValueHdr->valueFlags |= ValueProtected;            /* protect from user fiddling        */
  690.         RefDataObject(theValueHdr) = refDataObject;                    /* link value to the object            */
  691.     } else {                                                                                            /* use existing obj if >1st time*/
  692.         refDataValueHdr = getRecordingValueHdr(theValueHdr);/* get recording obj's value hdr*/
  693.         if (refDataValueHdr == NULL) return (NULL);                    /* something went wrong?                */
  694.     }
  695.         
  696.     container = refDataValueHdr->container;
  697.     CMextractData(container, theReferenceData, -4, &key);    /* don't allow a key of 0                */
  698.     if (key == 0) {
  699.         /* do not disable: Container_Disable(container);*/
  700.         ERROR1(CM_err_ZeroRefKey, CONTAINERNAME);
  701.         return (NULL);
  702.     }
  703.  
  704.     /* Search for theReferenceData key in the recording object...                                                    */
  705.     
  706.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  707.                                                 false, &refShadowEntry)) {
  708.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  709.         default:                      /* returned, just exit...                                                                                */
  710.         
  711.                                              return (NULL);
  712.         
  713.         case RefFound:         /* If theReferenceData key was found and the associated ID does    */
  714.                                              /* NOT need to be changed, just return the reference key. On the */
  715.                                              /* the other hand, if the key does need changing, then we FALL     */
  716.                                              /* THROUGH to the "not found" case to rerecord the list entry as */
  717.                                              /* if it's a new entry.  The offset is set to the proper place.     */
  718.                                                      
  719.                                              if (refedObject->objectID == objectID)                    /* if same ID...    */
  720.                                                  return ((CMReference *)theReferenceData);        /* ...return ref.    */
  721.         
  722.         case RefNotFound:  /* If theReferenceData key was not found (or we must change its     */
  723.                                              /* associated ID if it was found) then we (re)write the value         */
  724.                                              /* data entry with theReferenceData key and its (new) ID. The ID */
  725.                                              /* follows the key in the data (4 bytes each).                                        */
  726.          
  727.                                              refDataValueHdr->valueFlags &= ~ValueProtected;/* allow write         */
  728.                                              CMformatData(container, refData.key, -4, theReferenceData);
  729.                                              CMformatData(container, refData.objectID, 4, &refedObject->objectID);
  730.                                              CMWriteValueData((CMValue)refDataValueHdr, (CMPtr)&refData, offset, 8);
  731.                                              refDataValueHdr->valueFlags |= ValueProtected; /* reprotect value*/
  732.                                              
  733.                                              /* Create a new shadow list entry or change data in found entry.    */
  734.                                              /* All this info was generated by getReference() in the switch        */
  735.                                              /* statement above.                                                                                            */
  736.                                              
  737.                                              #if CMSHADOW_LIST
  738.                                              if (refShadowEntry == NULL) {                                    /* new entry            */
  739.                                                   refShadowEntry = appendShadowListEntry(container, RefShadowList(refDataValueHdr), key, refedObject->objectID);
  740.                                                   if (refShadowEntry == NULL) {
  741.                                                      cmDeleteRefDataShadowList(refDataValueHdr);
  742.                                                      return (NULL);
  743.                                                  }
  744.                                              } else                                                                                    /* change entry        */
  745.                                                   refShadowEntry->objectID = refedObject->objectID;
  746.                                              #endif
  747.                                              
  748.                                              return ((CMReference *)theReferenceData);            /* return ref.        */
  749.     } /* switch */
  750. }
  751.  
  752.  
  753. /*---------------------------------------------------------------------*
  754.  | getReferencedObject - retrieve a object (refNum) from a "reference" |
  755.  *---------------------------------------------------------------------*
  756.  
  757.  Converts an object "reference", created by CMNewReference() or CMSetReference(), back to
  758.  an object refNum, from theReferenceData key is (assumed) supplied from the specified
  759.  value.  If theReferenceData key is not found, NULL is returned.  However, if the key is 
  760.  found but the referenced object is NOT found, then either returns NULL an error depending
  761.  on the setting of reportUndefReferenceError.
  762. */
  763.  
  764. static CMObject getReferencedObject(CMValue value, CMReference CM_PTR theReferenceData,
  765.                                                                         CMBoolean reportUndefReferenceError)
  766. {
  767.     ContainerPtr                     container;
  768.     TOCObjectPtr                     refedObject;
  769.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  770.     CM_ULONG                          offset, key = 0;
  771.     CMObjectID                          objectID;
  772.     RefDataShadowEntryPtr refShadowEntry;
  773.     CM_CHAR                               idStr[15];
  774.  
  775.     ExitIfBadValue(value, NULL);                                                        /* validate value                            */
  776.     
  777.     theValueHdr = (TOCValueHdrPtr)value;
  778.     
  779.     /* If there is NO recording object, there are no references.  This is treated as a         */
  780.     /* "not found" condition.                                                                                                                            */
  781.     
  782.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  783.     if (refDataValueHdr == NULL) return (NULL);                            /* "not found" if no object        */
  784.         
  785.     container     = refDataValueHdr->container;
  786.  
  787.     /* Search for theReferenceData key in the recording object...                                                    */
  788.     
  789.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  790.                                                 false, &refShadowEntry)) {
  791.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  792.                                              /* returned, just exit...                                                                                */
  793.                                                     
  794.                                              return (NULL);
  795.         
  796.         case RefFound:         /* If theReferenceData key was found, use the associated ID to        */
  797.                                              /* find the object in the TOC. It must be found or it's an error.*/
  798.                                              /* If it is, return the object's refNum and treat it as a "use"     */
  799.                                              /* (i.e., increment the object's use count).                                            */
  800.  
  801.                                              refedObject = cmFindObject(theValueHdr->container->updatingContainer->toc, 
  802.                                                                                                      objectID);/* lookup ID*/
  803.                                              if (refedObject == NULL)                                                            /* ...oops!    */
  804.                                                   if (reportUndefReferenceError)
  805.                                                  { 
  806.                                                      /* do *not* disable the container */
  807.                                                    /*Container_Disable(container);*/
  808.                                                       ERROR2(CM_err_UndefReference, cmltostr(objectID, 1, false, idStr), CONTAINERNAME);
  809.                                                  }
  810.                                                  else
  811.                                                       return (NULL);
  812.                                              ++refedObject->useCount;                                                          /* count use*/
  813.                                              return ((CMObject)refedObject);
  814.         
  815.         case RefNotFound:     /* If theReferenceData key was not found just exit...                        */
  816.                          default:
  817.                                              return (NULL);
  818.     } /* switch */
  819. }
  820.  
  821. /*-----------------------------------------------------------------------*
  822.  | CMGetReferencedObject - retrieve a object (refNum) from a "reference" |
  823.  *-----------------------------------------------------------------------*
  824.  
  825.  Converts an object "reference", created by CMNewReference() or CMSetReference(), back to
  826.  an object refNum, from theReferenceData key is (assumed) supplied from the specified
  827.  value.  If theReferenceData key is not found, NULL is returned.  However, it is an error 
  828.  if the key is found but the referenced object is NOT found.
  829.  
  830.  Just call getReferencedObject with reportUndefReferenceError true;
  831. */
  832.  
  833. CMObject CM_FIXEDARGS CMGetReferencedObject(CMValue value, CMReference CM_PTR theReferenceData)
  834. {
  835.     return getReferencedObject(value, theReferenceData, true);
  836. }
  837.  
  838. /*-------------------------------------------------------------------------*
  839.  | CMGetReferenceForObject - retrieve a "reference" from a object (refNum) |
  840.  *-------------------------------------------------------------------------*
  841.  
  842.  This is the invert of CMGetReferencedObject.
  843.  
  844.  Given an object refNum, find theReferenceData key associated with this object ID.
  845. */
  846.  
  847. CMReference CM_PTR * CM_FIXEDARGS CMGetReferenceForObject(CMValue value, 
  848.                                                                                                                      CMObject referencedObject,
  849.                                                                                                                     CMReference CM_PTR theReferenceData)
  850. {
  851.     ContainerPtr                     container;
  852.     ContainerPtr                     targetContainer;
  853.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  854.     CM_ULONG                          offset, key = 0;
  855.     CMObjectID                          objectID;
  856.     RefDataShadowEntryPtr refShadowEntry;
  857.  
  858.     ExitIfBadValue(value, NULL);                                                        /* validate value                            */
  859.     ExitIfBadObject(referencedObject, NULL);                            /* validate referencedObject        */
  860.  
  861.     objectID = ((TOCObjectPtr)referencedObject)->objectID;
  862.  
  863.     theValueHdr = (TOCValueHdrPtr)value;
  864.     container     = theValueHdr->container->updatingContainer;
  865.     targetContainer = theValueHdr->container->targetContainer;
  866.     
  867.     if (targetContainer != ((TOCObjectPtr)referencedObject)->container->targetContainer) {
  868.         /* do not disable: Container_Disable(container);*/
  869.         ERROR2(CM_err_CantReference, CONTAINERNAMEx(container),
  870.                                                                  CONTAINERNAMEx(((TOCObjectPtr)referencedObject)->container));
  871.         return (NULL);
  872.     }
  873.     
  874.     /* If there is NO recording object, there are no references.  This is treated as a         */
  875.     /* "not found" condition.                                                                                                                            */
  876.     
  877.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  878.     if (refDataValueHdr == NULL) return (NULL);                            /* "not found" if no object        */
  879.         
  880.     /* Search for theReferenceData key in the recording object...                                                    */
  881.     
  882.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  883.                                                 true, &refShadowEntry)) {
  884.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  885.                                              /* returned, just exit...                                                                                */
  886.                                                     
  887.                                              return (NULL);
  888.         
  889.         case RefFound:         /* If object was found, use the associated ID to        */
  890.                                              /* find the object in the TOC. It must be found or it's an error.*/
  891.                                              
  892.                                              CMformatData(refDataValueHdr->container, theReferenceData, -4, &key); 
  893.                                              return ((CMReference *)theReferenceData);/* give back ptr to ref    */
  894.         
  895.         case RefNotFound:     /* If theReferenceData key was not found just exit...                        */
  896.                          default:
  897.                                              return (NULL);
  898.     } /* switch */
  899. }
  900.  
  901. /*------------------------------------------------------------*
  902.  | CMDeleteReference - delete an object/reference association |
  903.  *------------------------------------------------------------*
  904.  
  905.  Deletes a  single object "reference", created by CMNewReference() or CMSetReference()
  906.  assocated with the theReferenceData key (assumed) supplied from the specified value.
  907.  
  908.  The value's recording object's object/reference association list is searched for the 
  909.  specified theReferenceData key.  If it is found, the association is removed.  If it is
  910.  not found this routine does nothing.  Thus it is NOT considered an error if the
  911.  theReferenceData key is not found, or if found, that the associated object exist.
  912.  
  913.  Note, if all the references for the value's recording object are deleted, the recording
  914.  object itself is deleted.
  915. */
  916.  
  917. void CM_FIXEDARGS CMDeleteReference(CMValue value, CMReference CM_PTR theReferenceData)
  918. {
  919.     ContainerPtr                     container;
  920.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  921.     CM_ULONG                          offset, key = 0;
  922.     CMObjectID                          objectID;
  923.     RefDataShadowEntryPtr refShadowEntry;
  924.  
  925.     ExitIfBadValue(value, CM_NOVALUE);                                            /* validate value                            */
  926.     
  927.     theValueHdr = (TOCValueHdrPtr)value;
  928.     container     = theValueHdr->container->targetContainer;
  929.     
  930.     /* If there is NO recording object, there are no references.  This is treated as a         */
  931.     /* "not found" condition, so there's nothing to delete.                                                                */
  932.     
  933.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  934.     if (refDataValueHdr == NULL) return;                                        /* ...nothing to delete                */
  935.         
  936.     /* Search for theReferenceData key in the recording object...                                                    */
  937.     
  938.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  939.                                                 false, &refShadowEntry)) {
  940.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  941.                                              /* returned, just exit...                                                                                */
  942.                                                     
  943.                                              return;
  944.         
  945.         case RefFound:         /* If theReferenceData key was found, use its offset to delete        */
  946.                                              /* the reference (data).  If there are no more references in the */
  947.                                              /* recording object, the object itself is deleted.                                */
  948.              
  949. // the reference object is deleted if is is empty. However it is possible that during
  950. // updating, the reference object is deleted but we still point to it, until that problem
  951. // is solved, we leave it as object with empty value
  952. //
  953. //                                             if (refDataValueHdr->size == 8) {                /* if single item list    */
  954. //                                                  RefDataObject(theValueHdr)->objectFlags &= ~ProtectedObject;
  955. //                                                 CMDeleteObject((CMObject)RefDataObject(theValueHdr)); /* delete*/
  956. //                                                 RefDataObject(theValueHdr) = NULL;                    /* ...break link        */
  957. //                                             }
  958. //                                             else {                                 /* deprotect it during the Delete process */
  959. //                                               refDataValueHdr->valueFlags &= ~ValueProtected;
  960. //                                                 CMDeleteValueData((CMValue)refDataValueHdr, (CMCount)offset, 8);
  961. //                                               refDataValueHdr->valueFlags |= ValueProtected;
  962. //                                             }
  963.  
  964.                                              refDataValueHdr->valueFlags &= ~ValueProtected;
  965.                                              CMDeleteValueData((CMValue)refDataValueHdr, (CMCount)offset, 8);
  966.                                              refDataValueHdr->valueFlags |= ValueProtected;
  967.                                              
  968.                                              #if CMSHADOW_LIST
  969.                                              deleteShadowListEntry(refDataValueHdr, refShadowEntry);
  970.                                              #endif
  971.                                              
  972.                                              return;
  973.         
  974.         case RefNotFound:     /* If theReferenceData key was not found just exit...                        */
  975.                  default:
  976.                                              return;
  977.     } /* switch */
  978. }
  979.  
  980.  
  981. /*----------------------------------------------------------------------------------*
  982.  | CMCountReferences - return number of references recorded for the specified value |
  983.  *----------------------------------------------------------------------------------*
  984.  
  985.  Returns the total number of references recorded by CMNewReference() or CMSetReference()
  986.  for the specified value.
  987. */
  988.  
  989. CMCount CM_FIXEDARGS CMCountReferences(CMValue value)
  990. {
  991.     ContainerPtr     container;
  992.     TOCValueHdrPtr theValueHdr, refDataValueHdr;
  993.  
  994.     ExitIfBadValue(value, 0);                                                                /* validate value                            */
  995.     
  996.     theValueHdr = (TOCValueHdrPtr)value;
  997.     container     = theValueHdr->container->targetContainer;
  998.     
  999.     /* If there is NO recording object, there are no references...                                                */
  1000.     
  1001.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  1002.     if (refDataValueHdr == NULL) return (0);                                /* ...no references                        */
  1003.     
  1004.     return (refDataValueHdr->size / 8);                                            /* 8 bytes per reference            */
  1005. }
  1006.  
  1007.  
  1008. /*--------------------------------------------------------------------------*
  1009.  | getAdjacentReference - get the adjacent reference referred to by a value |
  1010.  *--------------------------------------------------------------------------*
  1011.  
  1012.  This routine returns the adjacent reference (key) following/preceding the currReferenceData 
  1013.  key for the specified value.  
  1014.  
  1015.  If currReferenceData is 0, then the first/last object reference key is returned.  If 
  1016.  currReferenceData is not 0, the next/previous reference key in sequence is returned.  In both cases
  1017.  the currReferenceData is CHANGED to the next/previous reference and the pointer to it returned as
  1018.  the function result.  NULL is returned and currReferenceData is undefined if there are no
  1019.  more references following/preceding currReferenceData.
  1020.  
  1021.  Note, the object refNum corresponding to each returned reference key mey be retrieved by
  1022.  calling CMGetReferencedObject().
  1023. */
  1024.  
  1025. static CMReference CM_PTR * CM_NEAR getAdjacentReference(CMValue value,
  1026.                                                                                                          CMReference CM_PTR currReferenceData,
  1027.                                                                                                          CMBoolean forward)
  1028. {
  1029.     ContainerPtr     container;
  1030.     TOCValueHdrPtr theValueHdr, refDataValueHdr;
  1031.     CM_ULONG           currKey;
  1032.  
  1033.     ExitIfBadValue(value, NULL);                                                        /* validate value                            */
  1034.     
  1035.     theValueHdr = (TOCValueHdrPtr)value;
  1036.     
  1037.     /* If there is NO recording object, there are no references...                                                */
  1038.     
  1039.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  1040.     if (refDataValueHdr == NULL) return (NULL);                            /* ...no references                        */
  1041.  
  1042.     container     = refDataValueHdr->container;
  1043.  
  1044.     /* If the passed key is 0, treat this as the first/last reference. The "next"                 */
  1045.     /* reference position is maintained in the recording value's refCon (it's available     */
  1046.     /* -- no one     else is using it in the recording value header).                                                */
  1047.     
  1048.     CMextractData(container, currReferenceData, 4, &currKey); /* convert key to internal    */
  1049.     
  1050.     /* From this point on we do the "get next" operations differently depending on                 */
  1051.     /* whether a shadow list exists for the recording object's value data list.  It need     */
  1052.     /* not exist if getReference() was never called or we're configured not to have a         */
  1053.     /* shadow list.  If we do have the list, we search it, rather than reading the data        */
  1054.     /* explicitly which is what we have to do if the list does not exist.                                    */
  1055.     
  1056.     #if CMSHADOW_LIST
  1057.     if (RefShadowList(refDataValueHdr) != NULL) {                        /* if shadow lists exists...    */
  1058.         
  1059.         /* When using the shadow list, the recording value's refCon is a pointer to the         */
  1060.         /* next shadow list entry.  When the key is 0 we know to init the pointer to the        */
  1061.         /* first shadow list entry.                                                                                                                    */
  1062.         
  1063.         if (currKey == 0)                                                                         /* if first time...                        */
  1064.             if (forward)    /* first item if we want next, last item if we want prev  */
  1065.                 refDataValueHdr->valueRefCon = (CMRefCon)cmGetListHead(RefShadowList(refDataValueHdr));
  1066.             else
  1067.                 refDataValueHdr->valueRefCon = (CMRefCon)cmGetListTail(RefShadowList(refDataValueHdr));
  1068.         
  1069.         /* If there is no "next" list entry, there are no more references...                                */
  1070.         
  1071.         if (refDataValueHdr->valueRefCon == NULL)
  1072.             return (NULL);
  1073.         
  1074.         /* The "next" pointer from the previous call is the "current" pointer for this call.*/
  1075.         /* Use it to convert the shadow list key of the entry pointed to to a CMReference.    */
  1076.         /* This is what we will give back to the user.                                                                            */
  1077.         
  1078.         CMformatData(container, currReferenceData, 4, 
  1079.                                  &((RefDataShadowEntryPtr)(refDataValueHdr->valueRefCon))->key);
  1080.         
  1081.         /* Update the pointer to the "next" shadow list entry for the next time around...        */
  1082.         
  1083.         if (forward)
  1084.             refDataValueHdr->valueRefCon = (CMRefCon)(cmGetNextListCell(refDataValueHdr->valueRefCon));
  1085.         else
  1086.             refDataValueHdr->valueRefCon = (CMRefCon)(cmGetPrevListCell(refDataValueHdr->valueRefCon));
  1087.  
  1088.         return ((CMReference *)currReferenceData);                        /* give back ptr to reference    */
  1089.     }  /* using shadow list */
  1090.     #endif
  1091.  
  1092.     /* If there is no shadow list, we have to do things the "hard way".  We have to             */
  1093.     /* explicitly read the recording object's value data list entries in succession. Here    */
  1094.     /* the refCon is still a "pointer" to the next shadow list entry.  But unlike the            */
  1095.     /* shadow list case, the refCon takes the form of the next value data offset to be         */
  1096.     /* used to read in the next entry.  As usual, a input key of 0 signals that this is     */
  1097.     /* the first call and thus to init the pointer to offset 0.                                                        */
  1098.  
  1099.     if (currKey == 0)                                                                             /* if first time...                        */
  1100.         if (forward)
  1101.             refDataValueHdr->valueRefCon = (CMRefCon)0;                        /* init value data offset     */
  1102.         else
  1103.             refDataValueHdr->valueRefCon = (CMRefCon)(refDataValueHdr->size - sizeof(ReferenceData));
  1104.  
  1105.     /* If the current offset is at the end of the data, there are no more references...        */
  1106.     
  1107.     if (forward) {
  1108.         if ((CM_ULONG)refDataValueHdr->valueRefCon >= refDataValueHdr->size)
  1109.             return (NULL);
  1110.     }
  1111.     else {
  1112.         if ((CM_ULONG)refDataValueHdr->valueRefCon < 0)
  1113.             return (NULL);
  1114.     }
  1115.     
  1116.     /* Read the next reference at the current offset and bump the offset for the next            */
  1117.     /* time.  Note, we only have to read the key, but we have to bump the offset by the     */
  1118.     /* entry size.  Also, since the key is already in CMReference format, we can return     */
  1119.     /* it to the caller as is.                                                                                                                        */
  1120.     
  1121.     if (CMReadValueData((CMValue)refDataValueHdr, (CMPtr)currReferenceData, 
  1122.                                             (CM_ULONG)refDataValueHdr->valueRefCon, sizeof(CMReference)) != sizeof(CMReference)) {
  1123.         /* do not disable container: Container_Disable(container); */
  1124.         ERROR1(CM_err_BadRefRead, CONTAINERNAME);
  1125.         return (NULL);
  1126.     }
  1127.     
  1128.     if (forward)
  1129.         refDataValueHdr->valueRefCon = (CMRefCon)((CM_ULONG)refDataValueHdr->valueRefCon
  1130.                                                                                                 + sizeof(ReferenceData));
  1131.     else
  1132.         refDataValueHdr->valueRefCon = (CMRefCon)((CM_ULONG)refDataValueHdr->valueRefCon
  1133.                                                                                                 - sizeof(ReferenceData));
  1134.     
  1135.     return ((CMReference *)currReferenceData);                            /* give back ptr to reference    */
  1136. }
  1137.                                                           
  1138. /*--------------------------------------------------------------------*
  1139.  | CMGetNextReference - get the next reference referred to by a value |
  1140.  *--------------------------------------------------------------------*
  1141.  
  1142.  Just call getAdjacentReference with forward = true.
  1143. */
  1144.  
  1145. CMReference CM_PTR * CM_FIXEDARGS CMGetNextReference(CMValue value,
  1146.                                                                                                          CMReference CM_PTR currReferenceData)
  1147. {
  1148.     return getAdjacentReference(value, currReferenceData, true);
  1149. }
  1150.  
  1151. /*-------------------------------------------------------------------------*
  1152.  | CMGetPrevtReference - get the previous reference referred to by a value |
  1153.  *-------------------------------------------------------------------------*
  1154.  
  1155.  Just call getAdjacentReference with forward = false.
  1156. */
  1157.  
  1158. CMReference CM_PTR * CM_FIXEDARGS CMGetPrevReference(CMValue value,
  1159.                                                                                                          CMReference CM_PTR currReferenceData)
  1160. {
  1161.     return getAdjacentReference(value, currReferenceData, false);
  1162. }
  1163.  
  1164. /*------------------------------------------------------------------------*
  1165.  | CMKeepObject - protect this object and associated object from deletion |
  1166.  *------------------------------------------------------------------------*
  1167.  
  1168.  Protect this object. Follow all the reference from this object and protect those objects
  1169.  as well. This is done recursively so all related object are protected. Once we make this
  1170.  call, then on closing all non-protected object are consider to be available for garbage
  1171.  collection and will be deleted.
  1172. */
  1173.  
  1174. void CM_FIXEDARGS CMKeepObject(CMObject theObject)
  1175. {
  1176.     CMProperty        currProperty;
  1177.     CMValue                currValue;
  1178.     CMReference        currRef;
  1179.     CMObject            refObj;
  1180.     TOCObjectPtr    typeObject;
  1181.  
  1182.     ExitIfBadObject(theObject, CM_NOVALUE);                    /* validate theObject                                    */
  1183.     
  1184.     /* only need to do it if it is not alreay protected                                                                        */
  1185.     /* also don't do it if this is only open for reading only                                                            */
  1186.     if (((((TOCObjectPtr)theObject)->objectFlags & ProtectedObject) == 0) && 
  1187.                 (((TOCObjectPtr)theObject)->container->updatingContainer->useFlags & kCMWriting)) {
  1188.         ((TOCObjectPtr)theObject)->objectFlags |= ProtectedObject;
  1189.         
  1190.         /* Once this is call, the container will be garbage collected on closing                        */
  1191.         ((TOCObjectPtr)theObject)->container->updatingContainer->deleteGarbage = true;
  1192.         
  1193.         /* go and located all the references and protected object referenced as well                */
  1194.         currProperty = NULL;
  1195.         do {
  1196.             currProperty = CMGetNextObjectProperty(theObject, currProperty);
  1197.             if (currProperty) {
  1198.                 /* We use this property, so protect it so it won't be deleted                                        */
  1199.                 ((TOCObjectPtr)currProperty)->objectFlags |= ProtectedObject;
  1200.                 currValue = NULL;
  1201.                 do {
  1202.                     currValue = CMGetNextValue(theObject, currProperty, currValue);
  1203.                     if (currValue) {
  1204.                         /* We use this type, so protect it so it won't be deleted                                        */
  1205.                         typeObject = cmFindObject(((TOCObjectPtr)theObject)->container->toc, 
  1206.                                                                             ((TOCValueHdrPtr)currValue)->typeID);
  1207.                         if (typeObject)
  1208.                             typeObject->objectFlags |= ProtectedObject;
  1209.                         if (CMCountReferences(currValue)) {                                /* we have references            */
  1210.                             memset(&currRef, 0, sizeof(CMReference));
  1211.                             while (*CMGetNextReference(currValue, currRef)) {        /* loop through refs    */
  1212.                                 refObj = getReferencedObject(currValue, currRef, false);
  1213.                                 if (refObj) {
  1214.                                     CMKeepObject(refObj);                                    /* recursively protect them all */
  1215.                                     CMReleaseObject(refObj);        /* decr use count incr by CMGetReferen... */
  1216.                                 }
  1217.                                 else                                    /* There is no such reference object, delete it */
  1218.                                     CMDeleteReference(currValue, currRef);
  1219.                             }
  1220.                         }
  1221.                     }
  1222.                 } while (currValue);                                                             /* iterated through all types */
  1223.             }
  1224.         } while (currProperty);                                                 /* iterated through all properties  */
  1225.     }
  1226. }
  1227.  
  1228.                                                             CM_END_CFUNCTIONS
  1229.